/***********************************************************************************
 * 文件名： mbre_link.c
 * 作者： 刘言
 * 版本： 1
 * 说明：
 * 		MBRE(ModBusRtuExt) 1.1 通信协议的链路层。
 *      接受来自物理层（端口驱动）的数据，判断数据包合法性和完整性，去除信封（头、长度、校验
 *  码），传送到上一层；
 *      同样，也将来自上一层的数据，封装信封，发到物理层。
 *      连接与断连检测，事件通知。
 * 修改记录：
 * 	2023/4/26: 初版。 刘言。
***********************************************************************************/
#include "mbre_link.h"
#include "components\polsys_lib\polsys_lib.h"
#include "polsys.h"

// 属性
ALIGN(4) static xdata u8 mRxBuff[_MBRE_LINK_RX_BUFF_SIZE];
static idata u16 mLength;
#ifdef _MBRE_LINK_USE_CRC16
static idata u16 mCheckSum;
#endif
static bit mRxFilish;
static idata u8 mRxFlag;
static idata u8 mRxTimeout = 0;
static bit mConnected = false;
static idata u8 mDisConTimeout = 0;
static idata u16 mBuffIndex = 0;
static idata polsys_task_t mMbreLinkTimer;   // 软定时器
static idata polsys_task_t mMbreLinkProData;    // 处理数据任务

#ifdef _MBRE_LINK_USE_BUFF_CHECK
static idata u16 mRxLen = 0; // mBuffIndex可能会被清零，单独记录已接收的长度
#endif



// 内部函数声明

static void Timer();
static void ProData();


// 对外接口函数

IN_LINE void MbreLink_Init()
{
    _MbreLink_PortInit();
    Polsys_TaskInit(&mMbreLinkTimer, Timer, 10);
    Polsys_TaskInit(&mMbreLinkProData, ProData, POLSYS_WAITING_FOREVER);
}

FORCE_IN_LINE mbre_data_t *MbreLink_GetReceiveData()
{
    return (mbre_data_t *)mRxBuff;
}

FORCE_IN_LINE u16 MbreLink_GetLength()
{
    return mLength;
}

// FORCE_IN_LINE void MbreLink_ClearBuff()
// {
// #ifdef _MBRE_LINK_USE_BUFF_CHECK
//     这里必须先算出已接收到的数据的校验码！！！！！！！！！！！！！
//     mBuffIndex = 0;
// #endif
// }

void MbreLink_SendPkg(u16 lenght, u8 *dat)
{
#ifdef _MBRE_LINK_USE_CRC16
    u16 crc16 = _Mbre_GetCRC16(dat, lenght);
#endif
    _MbreLink_PortSendByte(0x55);
    _MbreLink_PortSendByte(0xAA);
    _MbreLink_PortSendByte(lenght); // 协议规定低字节在前
    _MbreLink_PortSendByte(lenght >> 8);
#ifdef _MBRE_ONLY_SEND_BYTE
    while(lenght--)
	{
		_MbreLink_PortSendByte(*dat);
		dat++;
	}
#else
    _MbreLink_PortSend(dat, lenght);
#endif
#ifdef _MBRE_LINK_USE_CRC16
    _MbreLink_PortSendByte(crc16); // 协议规定低字节在前
    _MbreLink_PortSendByte(crc16 >> 8);
#endif
}

// 以下是需要外部调用的事件处理函数

// 由外部调用（一般是UART中断）来通知 Mbre 收到了新的数据（1个字节）。
// 外部调用事件：收到了新的数据（1个字节）
void MbreLink_EvReceivedData(u8 dat)
{
    mRxTimeout = 0; // 超时清零
    switch (mRxFlag)
    {
    case 0: //接收头H
        if (dat == 0x55)
        {
            mRxFlag = 1; //进入下一步
        }
        break;
    case 1: //接收头L
        if (dat == 0xAA)
        {
            mRxFlag = 2; //进入下一步
        }
        else if (dat == 0x55)
        {
            mRxFlag = 1; //继续这一步
        }
        else
        {
            mRxFlag = 0; //重新开始接收头H
            mBuffIndex = 0;
        }
        break;
    case 2: //接收长度
        mRxBuff[mBuffIndex++] = dat;
        if (mBuffIndex == 2)
        {
            mLength = *(u16 *)mRxBuff;
            if (mLength == 0)
            {
                mRxFlag = 0; //重新开始接收头H
            }
            else
            {
                if (mLength > _MBRE_LINK_RX_BUFF_SIZE)mLength = _MBRE_LINK_RX_BUFF_SIZE;
                mRxFlag = 3;
            }
            mBuffIndex = 0;
        }
        break;

    case 3: //接收数据区
        mRxBuff[mBuffIndex++] = dat;
#ifdef _MBRE_LINK_USE_BUFF_CHECK
        if (mBuffIndex == _MBRE_LINK_RX_BUFF_SIZE)
        {
            mCall(MBRE_EV_BUFF_FULL);
            mBuffIndex = 0;
        }
        mRxLen++; // mBuffIndex会被清零，单独记录已接收的长度
        if (mRxLen == mLength) // 数据区接收完毕
#else
        if (mBuffIndex == mLength) // 数据区接收完毕
#endif
        {
#ifdef _MBRE_LINK_USE_CRC16
            mRxFlag = 4;    // 接收校验码
#else
            mRxFilish = true; //标记一包接收完毕
            mRxFlag = 0;      //重新开始接收头H
#endif
            mBuffIndex = 0;
#ifdef _MBRE_LINK_USE_BUFF_CHECK
            mRxLen = 0;
#endif
        }
        break;
#ifdef _MBRE_LINK_USE_CRC16
    case 4: // 接收校验码
        ((u8 *)(&mCheckSum))[mBuffIndex++] = dat;
        if(mBuffIndex == 2) // 校验码接收完毕
        {
            mRxFilish = true; // 标记一包接收完毕
            mRxFlag = 0;      // 重新开始接收头H
            mBuffIndex = 0;
            Polsys_SetTaskReady(&mMbreLinkProData); 
        }
        break;
#endif
    default:
        mRxFlag = 0;
        mBuffIndex = 0;
#ifdef _MBRE_LINK_USE_BUFF_CHECK
        mRxLen = 0;
#endif
        break;
    }
}


void _Mbre_EvLinkEvent(mbre_event_t e);

// 10ms循环执行
static void Timer()
{
    // 接收超时控制
    if (mRxFlag > 0) // 正在接收
    {
        mRxTimeout++;
        if (mRxTimeout == 50) // 500ms未收到数据
        {
            _Mbre_EvLinkEvent(MBRE_EV_TIME_OUT); // 上报：接收超时
            mRxTimeout = 0;
            mRxFlag = 0;
            mBuffIndex = 0;
#ifdef _MBRE_LINK_USE_BUFF_CHECK
            mRxLen = 0;
#endif
        }
    }
    else
    {
        mRxTimeout = 0;
    }

    // 断连计时与连接状态
    if(mConnected)
    {
        if(mDisConTimeout < 255)mDisConTimeout++;
        if(mDisConTimeout == 200)   // 2s未收到校验通过的数据包
        {
            mConnected = false;
            _Mbre_EvLinkEvent(MBRE_EV_DISCONNECTED);    // 上报：断连，信号中断
        }
    }
}

// 处理收到的数据
static void ProData()
{
    // 接收完毕处理【考虑创建线程等待信号量增强实时性】
    if (mRxFilish)
    {
        mRxFilish = false;
#ifdef _MBRE_LINK_USE_CRC16
        if(_Mbre_GetCRC16(mRxBuff,mLength) != mCheckSum)
            _Mbre_EvLinkEvent(MBRE_EV_CHECK_ERROR); // 上报：校验错误
        else
#endif
        {
            mDisConTimeout = 0;
            if(!mConnected)
            {
                mConnected = true;
                _Mbre_EvLinkEvent(MBRE_EV_CONNECTED);    // 上报：已连接
            }
            if(mRxBuff[1] != MBRE_CMD_HEARTBEAT)    // 收到的不是心跳包
            {
                _Mbre_EvLinkEvent(MBRE_EV_RECEIVED_PKG);    // 上报：收到了一包数据
            }
        }
    }
}


